//
// Copyright (c) 2010 Luís Reis
//
// MIT License
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
using System;
using System.Collections.Generic;

namespace Pine.Lexer
{
	public static class LexUtils
	{
		private static IList<char> symbolicChars = new List<char>();
		private static IList<char> whitespaceChars = new List<char>();
		private static IList<string> keywords = new List<string>();
		
		static LexUtils(){
			InitializeSymbolicChars();
			InitializeWhitespaceChars();
			InitializeKeywords();
		}
		
		private static void InitializeSymbolicChars() {
			symbolicChars.Add('\\');
			symbolicChars.Add('.');
			symbolicChars.Add(',');
			symbolicChars.Add(':');
			symbolicChars.Add(';');
			symbolicChars.Add('<');
			symbolicChars.Add('>');
			symbolicChars.Add('=');
			symbolicChars.Add('!');
			symbolicChars.Add('?');
			symbolicChars.Add('-');
			symbolicChars.Add('+');
			symbolicChars.Add('*');
			symbolicChars.Add('/');
			symbolicChars.Add('~');
			symbolicChars.Add('&');
			symbolicChars.Add('|');
			symbolicChars.Add('%');
			symbolicChars.Add('(');
			symbolicChars.Add(')');
			symbolicChars.Add('{');
			symbolicChars.Add('}');
			symbolicChars.Add('[');
			symbolicChars.Add(']');
		}
		
		private static void InitializeWhitespaceChars() {
			whitespaceChars.Add(' ');
			whitespaceChars.Add('\r');
			whitespaceChars.Add('\n');
			whitespaceChars.Add('\t');
		}
		
		private static void InitializeKeywords()
		{
			keywords.Add("abstract");
			keywords.Add("array");
			keywords.Add("bool");
			keywords.Add("break");
			keywords.Add("callee");
			keywords.Add("case");
			keywords.Add("char");
			keywords.Add("class");
			keywords.Add("const");
			keywords.Add("continue");
			keywords.Add("default");
			keywords.Add("do");
			keywords.Add("else");
			keywords.Add("extends");
			keywords.Add("false");
			keywords.Add("final");
			keywords.Add("for");
			keywords.Add("function");
			keywords.Add("if");
			keywords.Add("in");
			keywords.Add("is");
			keywords.Add("null");
			keywords.Add("number");
			keywords.Add("private");
			keywords.Add("protected");
			keywords.Add("public");
			keywords.Add("range");
			keywords.Add("return");
			keywords.Add("static");
			keywords.Add("string");
			keywords.Add("super");
			keywords.Add("switch");
			keywords.Add("this");
			keywords.Add("true");
			keywords.Add("type");
			keywords.Add("var");
			keywords.Add("virtual");
			keywords.Add("while");
		}

		public static bool IsValidIdentifier(this char c) {
			return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || c == '_' || c == '$';
		}
		
		public static bool IsValidIdentifier(this int d) {
			if (d < 0 || d > 255)
				return false;
			return ((char) d).IsValidIdentifier();	
		}
		
		public static bool IsDigit(this char c)  {
			return Char.IsDigit(c);	
		}
		
		public static bool IsDigit(this int d) {
			if (d < 0 || d > 255)
				return false;
			return ((char) d).IsDigit();	
		}
		
		public static bool IsOctalDigit(this char c) {
			return c >= '0' && c <= '7';	
		}
		
		public static bool IsOctalDigit(this int d) {
			if (d < 0 || d > 255)
				return false;
			return ((char) d).IsOctalDigit();	
		}
		
		public static bool IsHexadecimalDigit(this char c) {
			return c.IsDigit() || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F');	
		}
		
		public static bool IsHexadecimalDigit(this int d) {
			if (d < 0 || d > 255)
				return false;
			return ((char) d).IsHexadecimalDigit();	
		}
		
		public static bool IsSymbolicCharacter(this char c) {
			return symbolicChars.Contains(c);
		}
		
		public static bool IsSymbolicCharacter(this int d) {
			if (d < 0 || d > 255)
				return false;
			return ((char) d).IsSymbolicCharacter();	
		}
		
		public static bool IsWhitespace(this char c) {
			return whitespaceChars.Contains(c);	
		}
		
		public static bool IsWhitespace(this int d) {
			if (d < 0 || d > 255)
				return false;
			return ((char) d).IsWhitespace();	
		}
		
		public static bool IsLineBreak(this char c) {
			return c == '\r' || c == '\n';	
		}
		
		public static bool IsLineBreak(this int d) {
			if (d < 0 || d > 255)
				return false;
			return ((char) d).IsLineBreak();	
		}
		
		public static bool IsKeyword(this string word) {
			if (word == null)
				throw new ArgumentNullException("word");
			
			return keywords.Contains(word);	
		}
		
		public static int ParseHexDigit(this char c) {
			if (c >= 'a' && c <= 'f')
				return (int) 10 + c - 'a';
			if (c >= 'A' && c <= 'F')
				return (int) 10 + c - 'A';
			return (int) c - '0';
		}
		
		public static int ParseHex(this string hexString) {
			if (hexString == null)
				throw new ArgumentNullException("hexString");
			
			int p = 0;
			bool neg = false;
			int cn = 0;
			
			if (hexString.Length > 1) {
				if (hexString[0] == '-') {
					neg = true;
					p = 1;
				}
			}
			
			for (; p < hexString.Length; ++p) {
				cn *= 16;
				cn += hexString[p].ParseHexDigit();
			}
			
			return neg ? -cn : cn;
		}
		
		public static int ParseOctal(this string octalString) {
			if (octalString == null)
				throw new ArgumentNullException("octalString");
			
			int p = 0;
			bool neg = false;
			int cn = 0;
			
			if (octalString.Length > 1) {
				if (octalString[0] == '-') {
					neg = true;
					p = 1;
				}
			}
			
			for (; p < octalString.Length; ++p) {
				cn *= 8;
				cn += octalString[p] - '0';
			}
			
			return neg ? -cn : cn;
		}
	}
}

